package event

import (
	"sync"

	"github.com/sirupsen/logrus"
)

type ProcessorManager struct {
	lock sync.Mutex
	// all the processors
	totalProcMap map[string]EventProcessor
	// processors by type
	procMap map[int]EventProcessor
	// child-parent relation
	childParentMap map[string]string
}

func NewProcessorManager() *ProcessorManager {
	mgr := &ProcessorManager{
		totalProcMap:   map[string]EventProcessor{},
		procMap:        map[int]EventProcessor{},
		childParentMap: map[string]string{},
	}

	return mgr
}

func (m *ProcessorManager) AddProcessor(proc EventProcessor) bool {
	m.lock.Lock()
	defer m.lock.Unlock()
	if _, ok := m.totalProcMap[proc.Name()]; ok {
		logrus.Errorf("processor %s already exists", proc.Name())
		return false
	}
	if !proc.IsMiddleware() {
		if _, ok := m.procMap[proc.Type()]; ok {
			logrus.Errorf("processor for type %d already exists, processor %s add failed", proc.Type(), proc.Name())
			return false
		}
		m.procMap[proc.Type()] = proc
	}
	m.totalProcMap[proc.Name()] = proc
	return true
}
func (m *ProcessorManager) AddProcessorWithParent(proc EventProcessor, parentName string) bool {
	if parent, ok := m.totalProcMap[parentName]; ok {
		if !parent.IsMiddleware() {
			logrus.Errorf("parent %s is not middleware", parentName)
			return false
		}
		if m.AddProcessor(proc) {
			m.lock.Lock()
			defer m.lock.Unlock()
			m.childParentMap[proc.Name()] = parentName
			return true
		}
		logrus.Errorf("parent %s is not middleware", parentName)
		return false
	}
	logrus.Errorf("parent %s not exist", parentName)
	return false
}

func (m *ProcessorManager) Process(e *Event) {
	procs := m.getProcessors(e)
	e.ChangeStatus(Processing)
	for _, p := range procs {
		p.Process(e)
		if e.Done() {
			return
		}
	}
}

func (m *ProcessorManager) getProcessors(e *Event) []EventProcessor {
	var typeProc EventProcessor
	if p, ok := m.procMap[e.Type]; ok {
		typeProc = p
	} else {
		typeProc, ok = m.totalProcMap[UnregisteredEventProcessor.Name()]
		if !ok {
			return []EventProcessor{UnregisteredEventProcessor}
		}
	}

	ret := []EventProcessor{}
	p := typeProc
	for {
		ret = append(ret, p)
		if pa, ok := m.childParentMap[p.Name()]; ok {
			p = m.totalProcMap[pa]
		} else {
			break
		}
	}
	// reverse
	for i, j := 0, len(ret)-1; i < j; i, j = i+1, j-1 {
		ret[i], ret[j] = ret[j], ret[i]
	}
	return ret
}
